Skip to content

Conversation

@NarayanBavisetti
Copy link
Collaborator

@NarayanBavisetti NarayanBavisetti commented Aug 19, 2025

Description

this pull request creates a new description and description version model.

Type of Change

  • Feature (non-breaking change which adds functionality)

Summary by CodeRabbit

  • New Features
    • Rich-text descriptions stored in multiple formats (HTML, JSON, binary) for flexible use.
    • Automatic plain-text extraction from rich text for improved search, display, and previews.
    • Version history for descriptions to track and retrieve past edits.
    • Workspace- and project-aware descriptions with created/updated/deleted timestamps and creator metadata.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds Description and DescriptionVersion Django models (JSON/HTML/binary/stripped fields, UUID PKs, audit FKs to users/project/workspace) with save-time HTML stripping, a migration creating descriptions and description_versions tables, and exposes the models via the package init.

Changes

Cohort / File(s) Summary
Description feature models
apps/api/plane/db/models/description.py
Adds Description and DescriptionVersion models subclassing WorkspaceBaseModel. New fields: description_json, description_html, description_binary, description_stripped. DescriptionVersion adds FK description (related_name="versions"). Both implement save() to compute description_stripped via strip_tags. Meta sets verbose names, db_table, ordering.
DB migration
apps/api/plane/db/migrations/0101_description_descriptionversion.py
New migration creating descriptions and description_versions tables with UUID primary keys, audit timestamps, nullable deleted_at, JSON/Text/Binary fields with defaults, FKs to users/project/workspace/description, specified on_delete behaviors and related_name patterns, and ordering by -created_at. Depends on prior migration 0100.
Models package exports
apps/api/plane/db/models/__init__.py
Adds exports by importing Description and DescriptionVersion from .description.

Sequence Diagram(s)

sequenceDiagram
    actor Caller
    participant Model as Description / DescriptionVersion
    participant Stripper as strip_tags
    participant ORM as Django ORM
    participant DB as Database

    Caller->>Model: save()
    activate Model
    Model->>Stripper: strip_tags(description_html)
    Stripper-->>Model: description_stripped
    Model->>ORM: validate & prepare fields
    ORM->>DB: INSERT / UPDATE
    DB-->>ORM: OK
    ORM-->>Model: saved
    deactivate Model
    Model-->>Caller: return instance
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I nibble HTML, strip tags with care,
Store JSON crumbs and binary lair.
Versions nest softly in workspace burrows,
UUIDs hop through DB furrows.
A rabbit-approved commit — tiny, merry, fair. 🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 966b0ff and 6a955b7.

📒 Files selected for processing (1)
  • apps/api/plane/db/models/description.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/db/models/description.py
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-description-models

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces new Django models for managing descriptions and their versions, providing a structured way to store and track changes to issue descriptions across different formats (JSON, HTML, binary, and stripped text).

  • Adds Description and DescriptionVersion models with support for multiple content formats
  • Implements automatic HTML tag stripping in the save method for both models
  • Includes database migration to create the necessary tables and relationships

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
apps/api/plane/db/models/description.py Creates new Description and DescriptionVersion models with workspace relationships and content processing
apps/api/plane/db/models/init.py Exports the new models for use throughout the application
apps/api/plane/db/migrations/0101_description_descriptionversion.py Database migration to create tables for the new models with proper foreign key relationships

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
apps/api/plane/db/models/__init__.py (1)

87-87: Expose Description models here is fine; watch for potential confusion with IssueDescriptionVersion.

Importing and re-exporting Description and DescriptionVersion is consistent with the rest of this module. Small nit: we now have both IssueDescriptionVersion and DescriptionVersion in the same namespace, which are distinct but similarly named. This is OK, but be mindful in call sites to import the intended one explicitly to avoid mistakes.

apps/api/plane/db/models/description.py (2)

19-24: Consider adding a composite index to match likely query patterns.

Common access patterns will likely filter by workspace and order by created_at. A composite index helps.

Suggested Meta addition:

     class Meta:
         verbose_name = "Description"
         verbose_name_plural = "Descriptions"
         db_table = "descriptions"
         ordering = ("-created_at",)
+        indexes = [
+            models.Index(fields=["workspace", "created_at"], name="descr_ws_created_idx"),
+        ]

If you want, I can update the model and generate the migration for this index.


51-56: Add the same composite index here for parity and query performance.

     class Meta:
         verbose_name = "Description Version"
         verbose_name_plural = "Description Versions"
         db_table = "description_versions"
         ordering = ("-created_at",)
+        indexes = [
+            models.Index(fields=["workspace", "created_at"], name="descrv_ws_created_idx"),
+            models.Index(fields=["description", "created_at"], name="descrv_desc_created_idx"),
+        ]
apps/api/plane/db/migrations/0101_description_descriptionversion.py (2)

88-94: Optionally add indexes aligned with expected access patterns.

If you adopt the indexes suggested in the model Meta, regenerate this migration to include them. This will speed queries like “all descriptions in a workspace sorted by created_at” and “versions for a description sorted by created_at.”

I can add the indexes to the models and generate an updated migration if you’d like.


115-124: Minor: primary_key already implies unique + index; redundant flags on id can be avoided (not a blocker).

The UUIDField includes primary_key=True which makes it unique and indexed by default. The duplicated unique/db_index often comes from a shared base; no action required unless you are touching the base model.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d8f58d2 and 83e9b20.

📒 Files selected for processing (3)
  • apps/api/plane/db/migrations/0101_description_descriptionversion.py (1 hunks)
  • apps/api/plane/db/models/__init__.py (1 hunks)
  • apps/api/plane/db/models/description.py (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/api/plane/db/models/description.py (1)

15-17: Ignore duplicate workspace field suggestion – this override is intentional

The workspace field on Description(WorkspaceBaseModel) is deliberately overridden to customize its related_name="descriptions". Django allows subclasses of an abstract base model to redefine inherited fields for precisely this purpose. No removal is necessary.

Likely an incorrect or invalid review comment.

apps/api/plane/db/migrations/0101_description_descriptionversion.py (1)

140-146: FK string targets look correct; ensure app label/model name resolution matches runtime.

to="db.description" and to="db.workspace" align with Django’s migration conventions. No issues expected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
apps/api/plane/db/models/description.py (3)

32-35: Docstring should reflect version-tracking purpose

Clarify that this model stores historical versions linked to Description.

 class DescriptionVersion(WorkspaceBaseModel):
-    """
-    DescriptionVersion is a model that stores the description of an issue.
-    """
+    """
+    DescriptionVersion stores historical versions of a Description for auditing
+    and change tracking over time.
+    """

22-29: Optional: DRY the save logic via a small mixin

Both models duplicate identical save logic. A simple abstract mixin keeps it in one place.

Alternative to the two save() patches above:

@@
-from django.db import models
+from django.db import models
 from django.utils.html import strip_tags
 from .workspace import WorkspaceBaseModel
 
 
+class DescriptionHtmlStripMixin(models.Model):
+    class Meta:
+        abstract = True
+
+    # Override these in subclasses if field names differ
+    _html_field = "description_html"
+    _stripped_field = "description_stripped"
+
+    def save(self, *args, **kwargs):
+        update_fields = kwargs.get("update_fields")
+        if update_fields is None or self._html_field in update_fields:
+            html = getattr(self, self._html_field, "") or ""
+            plain = strip_tags(html).strip()
+            setattr(self, self._stripped_field, plain or None)
+            if update_fields is not None:
+                s = set(update_fields)
+                s.add(self._stripped_field)
+                kwargs["update_fields"] = s
+        super().save(*args, **kwargs)
+
@@
-class Description(WorkspaceBaseModel):
+class Description(DescriptionHtmlStripMixin, WorkspaceBaseModel):
@@
-    def save(self, *args, **kwargs):
-        # Strip the html tags using html parser
-        self.description_stripped = (
-            None
-            if (self.description_html == "" or self.description_html is None)
-            else strip_tags(self.description_html)
-        )
-        super(Description, self).save(*args, **kwargs)
+    # save() inherited from DescriptionHtmlStripMixin
@@
-class DescriptionVersion(WorkspaceBaseModel):
+class DescriptionVersion(DescriptionHtmlStripMixin, WorkspaceBaseModel):
@@
-    def save(self, *args, **kwargs):
-        # Strip the html tags using html parser
-        self.description_stripped = (
-            None
-            if (self.description_html == "" or self.description_html is None)
-            else strip_tags(self.description_html)
-        )
-        super(DescriptionVersion, self).save(*args, **kwargs)
+    # save() inherited from DescriptionHtmlStripMixin

Also applies to: 51-58


51-58: Mirror robust save(update_fields=...) logic for DescriptionVersion

Same issues as Description.save. Normalize blanks to None and ensure derived field is persisted on partial updates, recomputing only when HTML changes.

     def save(self, *args, **kwargs):
-        # Strip the html tags using html parser
-        self.description_stripped = (
-            None
-            if (self.description_html == "" or self.description_html is None)
-            else strip_tags(self.description_html)
-        )
-        super(DescriptionVersion, self).save(*args, **kwargs)
+        update_fields = kwargs.get("update_fields")
+        if update_fields is None or "description_html" in update_fields:
+            plain = strip_tags(self.description_html or "").strip()
+            self.description_stripped = plain or None
+            if update_fields is not None:
+                s = set(update_fields)
+                s.add("description_stripped")
+                kwargs["update_fields"] = s
+        super(DescriptionVersion, self).save(*args, **kwargs)
🧹 Nitpick comments (2)
apps/api/plane/db/models/description.py (2)

14-14: Mark derived field as non-editable

description_stripped is computed; setting editable=False avoids accidental admin/form edits.

-    description_stripped = models.TextField(blank=True, null=True)
+    description_stripped = models.TextField(blank=True, null=True, editable=False)

Apply the same change to DescriptionVersion.

Also applies to: 43-43


12-12: Consider default "" for HTML instead of "

"

Using an empty string as the default avoids sentinel HTML. Your new save() logic will already normalize blanks, but this change improves data cleanliness.

-    description_html = models.TextField(blank=True, default="<p></p>")
+    description_html = models.TextField(blank=True, default="")

Apply the same change to DescriptionVersion.

Also applies to: 41-41

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83e9b20 and 966b0ff.

📒 Files selected for processing (2)
  • apps/api/plane/db/migrations/0101_description_descriptionversion.py (1 hunks)
  • apps/api/plane/db/models/description.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/db/migrations/0101_description_descriptionversion.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript)

@NarayanBavisetti NarayanBavisetti changed the title chore: created new description model [WEB-4716] chore: created new description model Aug 19, 2025
@makeplane
Copy link

makeplane bot commented Aug 19, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

@sriramveeraghanta sriramveeraghanta merged commit 6398fc3 into preview Aug 19, 2025
5 of 7 checks passed
@sriramveeraghanta sriramveeraghanta deleted the feat-description-models branch August 19, 2025 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants